import { AlignType, ClassName, CtrlType, Events, Shape } from "@/Constants";
import { IPoint, IVctor } from "../../Interface";
import { Utils } from "../../utils";
import Rect from "../basic-shape/Rect";
import Feature from "../Feature";
import RCtrlPnt from "./ctrl-pnts/RCtrlPnt";
import CtrlPnt from "./ctrl-pnts/CtrlPnt";
import Pnt from "./Pnt";
import BCtrlPnt from "./ctrl-pnts/BCtrlPnt";
import Line from "../basic-shape/Line";

let lastDx = 0;
let lastDy = 0;

// 包围盒元素, 形变(放大,缩小)元素用 bbox 始终是矩形
export default class Bbox extends Rect {

    static isAbsorbAngle = false; // 是否旋转角度的吸附
    static isKeepRatio = true; // 是否按宽高比例缩放
    static ctrlPSize = 14; // 控制点大小

    ratio: number;  // 宽高比
    target: Feature;  // 宽高比

    constructor(target: Feature) {   // 相对坐标
        super(0, 0, 0, 0);
        this.pointArr = this.getBboxWrapPnts(target);
        this.isFixedPos = target.isFixedPos;
        this.group = target.group;
        this.angle = target.angle;
        this.cbDrag = target.cbDrag;
        this.cbTranslate = target.cbTranslate;
        this.className = ClassName.BBOX;
        this.target = target;
        this.visible = false;  // 跳过渲染,可以让bbox选中时,层级不在最上面
        this.addChild(target, false);  // 改变了parent 导致层级失效
        this.createCtrlPnts();
        const { width, height } = this.getBboxSize();
        this.setPointArrPer(this, width, height);
        this.ratio = this.getRatio();
        Feature.Gls.addFeature(this);
        target.on(Events.CHANGE, (e: any) => {
            this.resize.call(this)
        })
    }

    // 获取父元素pointArr所有点距离包围盒上下百分比
    setPointArrPer(feature: Feature, width = 0, height = 0) {
        feature._pntPerOfBBox.toLeft = []
        feature._pntPerOfBBox.toRight = []
        feature.pointArr.forEach(p => {
            const ldx = Utils.getLenOfPntToLine(p, this.pointArr[0], this.pointArr[3]);
            const ldy = Utils.getLenOfPntToLine(p, this.pointArr[0], this.pointArr[1]);
            feature._pntPerOfBBox.toLeft.push({
                x: ldx / width,
                y: ldy / height,
            })
            const rdx = Utils.getLenOfPntToLine(p, this.pointArr[1], this.pointArr[2]);
            const rdy = Utils.getLenOfPntToLine(p, this.pointArr[2], this.pointArr[3]);
            feature._pntPerOfBBox.toRight.push({
                x: rdx / width,
                y: rdy / height,
            })
        })
        feature.children.forEach(f => {
            this.setPointArrPer(f, width, height);
        })
    }

    computeNewPnts(feature: Feature, moved = 0, type: AlignType, ctrlPnt: Pnt, vcts: { x: IVctor, y: IVctor }) {
        const { x: vctX, y: vctY } = vcts
        if (moved == 0) return
        if (feature != Feature.Gls.focusNode && !(feature instanceof RCtrlPnt)) {  // 不能是控制点自己和旋转控制点
            feature.pointArr.forEach((p, i) => {
                let newPnt = { x: 0, y: 0 }
                switch (type) {
                    case AlignType.LEFT:
                        newPnt = Utils.getPntInVct(p, vctX, moved * -feature._pntPerOfBBox.toRight[i].x);
                        break;
                    case AlignType.RIGHT:
                        newPnt = Utils.getPntInVct(p, vctX, moved * feature._pntPerOfBBox.toLeft[i].x);
                        break;
                    case AlignType.TOP:
                        newPnt = Utils.getPntInVct(p, vctY, moved * -feature._pntPerOfBBox.toRight[i].y);
                        break;
                    case AlignType.BOTTOM:
                        newPnt = Utils.getPntInVct(p, vctY, moved * feature._pntPerOfBBox.toLeft[i].y);
                        break;
                    default:
                        break;
                }
                p.x = newPnt.x;
                p.y = newPnt.y;
            })
            feature.dispatch(new CustomEvent(Events.RESIZE, { detail: { target: feature, trigger: ctrlPnt } }))
            if (feature.cbTransformChild) feature.children.forEach(f => {
                this.computeNewPnts(f, moved, type, ctrlPnt, vcts);
            })
        }
    }

    // 初始化添加控制点
    createCtrlPnts(target = this.target) {
        let vctX: IVctor;
        let vctY: IVctor;
        if (target.ctrlTypes.includes(CtrlType.SIZE_CTRL)) {
            this.pointArr.forEach((p, i) => {
                const ctrlPnt = new CtrlPnt(this, this.pointArr[i].x, this.pointArr[i].y, Bbox.ctrlPSize, Bbox.ctrlPSize);
                ctrlPnt.type = CtrlType.SIZE_CTRL;
                ctrlPnt.name = `pCtrlp${i}`
                ctrlPnt.on(Events.DRAG_START, () => {
                    lastDx = 0
                    lastDy = 0
                    const { x, y } = this.getVctor();
                    vctX = x;
                    vctY = y;
                })
                ctrlPnt.on(Events.DRAG, () => this.toScaleSize(ctrlPnt, { x: vctX, y: vctY }))  // 控制点一律使用DRAG事件,否则会意外旋转情况
                ctrlPnt.on(Events.DRAG_END, () => this.resize())
            })
        }
        // 旋转点
        if (target.ctrlTypes.includes(CtrlType.ANGLE_CTRL)) {
            const rCtrlP = new RCtrlPnt(this, this.getRotatePnt.bind(this), Bbox.ctrlPSize, Bbox.ctrlPSize);
            rCtrlP.type = CtrlType.ANGLE_CTRL;
            rCtrlP.name = 'rCtrlp'
            rCtrlP.on(Events.DRAG, () => {
                const bboxPos = Feature.getCenterPos(this.pointArr); // bbox的中心点
                const bctrlPos = Feature.getCenterPos(rCtrlP.pointArr); // 旋转控制点的中心点
                const vct1: IVctor = [0, -100];
                const vct2 = Utils.getVector(bboxPos, bctrlPos);
                let angle = Utils.getRotateAng(vct1, vct2);
                const offsetAngle = angle - rCtrlP.lastAngle;
                this.rotate(offsetAngle);
                if (Bbox.isAbsorbAngle) { // 角度吸附
                    let absorbAngle = 0
                    if (this.angle <= 2 && !absorbAngle) {
                        absorbAngle = 0 - this.angle
                    }
                    if (this.angle >= 43 && this.angle <= 47 && !absorbAngle) {
                        absorbAngle = 45 - this.angle
                    }
                    if (this.angle >= 88 && this.angle <= 92 && !absorbAngle) {
                        absorbAngle = 90 - this.angle
                    }
                    if (this.angle >= 133 && this.angle <= 137 && !absorbAngle) {
                        absorbAngle = 135 - this.angle
                    }
                    if (this.angle >= 178 && this.angle <= 182 && !absorbAngle) {
                        absorbAngle = 180 - this.angle
                    }
                    if (this.angle >= 223 && this.angle <= 227 && !absorbAngle) {
                        absorbAngle = 225 - this.angle
                    }
                    if (this.angle >= 268 && this.angle <= 272 && !absorbAngle) {
                        absorbAngle = 270 - this.angle
                    }
                    if (this.angle >= 313 && this.angle <= 317 && !absorbAngle) {
                        absorbAngle = 315 - this.angle
                    }
                    if (this.angle >= 358 && !absorbAngle) {
                        absorbAngle = 360 - this.angle
                    }
                    this.rotate(absorbAngle);
                    angle += absorbAngle;
                }
                rCtrlP.lastAngle = angle;
            })
            rCtrlP.on(Events.DRAG_END, () => this.resize())
        }

        if (target.ctrlTypes.includes(CtrlType.WIDTH_CTRL)) {
            // 左边
            const leftCtrlPos = this.getLeftMidPnt();
            const bCtrlP2 = new CtrlPnt(this, leftCtrlPos.x, leftCtrlPos.y, Bbox.ctrlPSize, Bbox.ctrlPSize);
            bCtrlP2.type = CtrlType.WIDTH_CTRL;
            bCtrlP2.name = `pCtrlp${4}`
            bCtrlP2.on(Events.DRAG_START, () => {
                lastDx = 0
                const { x, y } = this.getVctor();
                vctX = x;
                vctY = y;
            })
            bCtrlP2.on(Events.DRAG, () => {
                const ctrlPos = Feature.getCenterPos(bCtrlP2.pointArr);  // 当前控制点的中心点
                let dx = Utils.getLenOfPntToLine(ctrlPos, this.pointArr[1], this.pointArr[2]); // 控制点到对边的距离，移动的距离
                const dir = Utils.isPntLeftOrRightOfLine(ctrlPos, this.pointArr[1], this.pointArr[2]);
                if (dir == 'right') dx = -dx;
                if (lastDx) {
                    this.computeNewPnts(this, dx - lastDx, AlignType.LEFT, bCtrlP2, { x: vctX, y: vctY })
                }
                lastDx = dx;
                this.ratio = this.getRatio();
            })
            bCtrlP2.on(Events.DRAG_END, () => this.resize())

            // 右边
            const rightCtrlPos = this.getRightMidPnt();
            const bCtrlP3 = new CtrlPnt(this, rightCtrlPos.x, rightCtrlPos.y, Bbox.ctrlPSize, Bbox.ctrlPSize);
            bCtrlP3.type = CtrlType.WIDTH_CTRL;
            bCtrlP3.name = `pCtrlp${5}`;
            bCtrlP3.on(Events.DRAG_START, () => {
                lastDx = 0
                const { x, y } = this.getVctor();
                vctX = x;
                vctY = y;
            })
            bCtrlP3.on(Events.DRAG, () => {
                const ctrlPos = Feature.getCenterPos(bCtrlP3.pointArr);  // 当前控制点的中心点
                let dx = Utils.getLenOfPntToLine(ctrlPos, this.pointArr[0], this.pointArr[3]);
                const dir = Utils.isPntLeftOrRightOfLine(ctrlPos, this.pointArr[0], this.pointArr[3]);
                if (dir == 'left') dx = -dx;
                if (lastDx) {
                    this.computeNewPnts(this, dx - lastDx, AlignType.RIGHT, bCtrlP3, { x: vctX, y: vctY })
                }
                lastDx = dx;
                this.ratio = this.getRatio();
            })
            bCtrlP3.on(Events.DRAG_END, () => this.resize())
        }

        if (target.ctrlTypes.includes(CtrlType.HEIGHT_CTRL)) {
            // 上边
            const topCtrlPos = this.getTopMidPnt();
            const bCtrlP4 = new CtrlPnt(this, topCtrlPos.x, topCtrlPos.y, Bbox.ctrlPSize, Bbox.ctrlPSize);
            bCtrlP4.type = CtrlType.HEIGHT_CTRL;
            bCtrlP4.name = `pCtrlp${6}`
            bCtrlP4.on(Events.DRAG_START, () => {
                lastDy = 0
                const { x, y } = this.getVctor();
                vctX = x;
                vctY = y;
            })
            bCtrlP4.on(Events.DRAG, () => {
                const ctrlPos = Feature.getCenterPos(bCtrlP4.pointArr);  // 当前控制点的中心点
                let dy = Utils.getLenOfPntToLine(ctrlPos, this.pointArr[2], this.pointArr[3]);
                const dir = Utils.isPntLeftOrRightOfLine(ctrlPos, this.pointArr[2], this.pointArr[3]);
                if (dir == 'right') dy = -dy;
                if (lastDy) {
                    this.computeNewPnts(this, dy - lastDy, AlignType.TOP, bCtrlP4, { x: vctX, y: vctY })
                }
                lastDy = dy;
                this.ratio = this.getRatio();
            })
            bCtrlP4.on(Events.DRAG_END, () => this.resize())

            // 下边
            const bottomCtrlPos = this.getBottomMidPnt();
            const bCtrlP5 = new CtrlPnt(this, bottomCtrlPos.x, bottomCtrlPos.y, Bbox.ctrlPSize, Bbox.ctrlPSize);
            bCtrlP5.type = CtrlType.HEIGHT_CTRL;
            bCtrlP5.name = `pCtrlp${7}`
            bCtrlP5.on(Events.DRAG_START, () => {
                lastDy = 0
                const { x, y } = this.getVctor();
                vctX = x;
                vctY = y;
            })
            bCtrlP5.on(Events.DRAG, () => {
                const ctrlPos = Feature.getCenterPos(bCtrlP5.pointArr);  // 当前控制点的中心点
                let dy = Utils.getLenOfPntToLine(ctrlPos, this.pointArr[0], this.pointArr[1]);
                const dir = Utils.isPntLeftOrRightOfLine(ctrlPos, this.pointArr[0], this.pointArr[1]);
                if (dir == 'right') dy = -dy;
                if (lastDy) {
                    this.computeNewPnts(this, dy - lastDy, AlignType.BOTTOM, bCtrlP5, { x: vctX, y: vctY })
                }
                lastDy = dy;
                this.ratio = this.getRatio();
            })
            bCtrlP5.on(Events.DRAG_END, () => this.resize())
        }
        if (target.ctrlTypes.includes(CtrlType.PNT_CTRL)) { // 线的点坐标控制点
            const originPnts = target.pointArr.filter(f => !f.generated);
            originPnts.forEach((curP, i) => {
                const ctrlPnt = new CtrlPnt(this, curP.x, curP.y)
                ctrlPnt.fillStyle = '#BB8FCE';
                ctrlPnt.strokeStyle = '#7D3C98';
                ctrlPnt.shape = Shape.CIRCLE;
                ctrlPnt.hoverStyle = ctrlPnt.focusStyle = '#7D3C98';
                ctrlPnt.on(Events.DRAG, () => {
                    const originPnts2 = target.pointArr.filter(f => !f.generated);
                    if (originPnts2[i]) {
                        const center = Feature.getCenterPos(ctrlPnt.pointArr)
                        originPnts2[i].x = center.x
                        originPnts2[i].y = center.y
                        this.resize(target);
                        if (target instanceof Line && target.getGeneratePointArrFn) {
                            target.pointArr = target.getGeneratePointArrFn(originPnts2)
                        }
                    }
                    target.dispatch(new CustomEvent(Events.RESIZE, { detail: { target } }))
                })
                ctrlPnt.on(Events.DRAG_END, () => {
                    let { width, height } = this.getBboxSize();
                    this.setPointArrPer(this, width, height)
                    this.ratio = this.getRatio();
                    target.dispatch(new CustomEvent(Events.RESIZE, { detail: { target } }))
                })
            })
        }
        if (target.ctrlTypes.includes(CtrlType.BEZIER_CTRL)) {  // 贝塞尔曲线控制点
            target.bezierPnts.forEach((ctrlp, i) => {
                const bezierP = new BCtrlPnt(this, ctrlp.x, ctrlp.y)
                bezierP.on(Events.DRAG, () => {
                    target.bezierPnts[i] = bezierP.pointArr[0];
                    const originPnts = target.pointArr.filter(f => !f.generated);
                    if (target.getGeneratePointArrFn) {
                        target.pointArr = target.getGeneratePointArrFn(originPnts)
                    }
                    this.resize(target);
                    target.dispatch(new CustomEvent(Events.RESIZE, { detail: { target } }))
                })
                bezierP.on(Events.DRAG_END, () => {
                    let { width, height } = this.getBboxSize();
                    this.setPointArrPer(this, width, height)
                    this.ratio = this.getRatio();
                    target.dispatch(new CustomEvent(Events.RESIZE, { detail: { target } }))
                })
            })
        }
    }

    toScaleSize(ctrlPnt: Pnt, vcts: { x: IVctor, y: IVctor }) {
        const ctrlPos = ctrlPnt.pointArr[0];  // 当前控制点的中心点
        let dx = 0;
        let dy = 0;
        switch (ctrlPnt.name) {
            case 'pCtrlp0':  // 左上角
                {
                    dx = Utils.getLenOfPntToLine(ctrlPos, this.pointArr[1], this.pointArr[2]); // 控制点到对边的距离，移动的距离
                    dy = Bbox.isKeepRatio ? dx / this.ratio : Utils.getLenOfPntToLine(ctrlPos, this.pointArr[2], this.pointArr[3]);
                    if (lastDx && lastDy) {
                        if (dx != 0) {
                            this.computeNewPnts(this, dx - lastDx, AlignType.LEFT, ctrlPnt, vcts)
                        }
                        if (dy != 0) {
                            this.computeNewPnts(this, dy - lastDy, AlignType.TOP, ctrlPnt, vcts)
                        }
                    }
                    break;
                }
            case 'pCtrlp1':  // 右上角
                {
                    dx = Utils.getLenOfPntToLine(ctrlPos, this.pointArr[3], this.pointArr[0]); // 控制点到对边的距离，移动的距离
                    dy = Bbox.isKeepRatio ? dx / this.ratio : Utils.getLenOfPntToLine(ctrlPos, this.pointArr[2], this.pointArr[3]);
                    if (lastDx && lastDy) {
                        if (dx != 0) {
                            this.computeNewPnts(this, dx - lastDx, AlignType.RIGHT, ctrlPnt, vcts)
                        }
                        if (dy != 0) {
                            this.computeNewPnts(this, dy - lastDy, AlignType.TOP, ctrlPnt, vcts)
                        }
                    }
                    break;
                }
            case 'pCtrlp2':  // 右下角
                {
                    dx = Utils.getLenOfPntToLine(ctrlPos, this.pointArr[3], this.pointArr[0]); // 控制点到对边的距离，移动的距离
                    // const dir = Utils.isPntLeftOrRightOfLine(ctrlPos, this.pointArr[3], this.pointArr[0]);
                    // if (dir == 'right') dx = -dx;
                    dy = Bbox.isKeepRatio ? dx / this.ratio : Utils.getLenOfPntToLine(ctrlPos, this.pointArr[0], this.pointArr[1]);
                    // const dir2 = Utils.isPntLeftOrRightOfLine(ctrlPos, this.pointArr[0], this.pointArr[1]);
                    // if (dir2 == 'right') dy = -dy;
                    if (lastDx && lastDy) {
                        if (dx != 0) {
                            this.computeNewPnts(this, dx - lastDx, AlignType.RIGHT, ctrlPnt, vcts)
                        }
                        if (dy != 0) {
                            this.computeNewPnts(this, dy - lastDy, AlignType.BOTTOM, ctrlPnt, vcts)
                        }
                    }
                    break;
                }
            case 'pCtrlp3':  // 左下角
                {
                    dx = Utils.getLenOfPntToLine(ctrlPos, this.pointArr[1], this.pointArr[2]); // 控制点到对边的距离，移动的距离
                    dy = Bbox.isKeepRatio ? dx / this.ratio : Utils.getLenOfPntToLine(ctrlPos, this.pointArr[0], this.pointArr[1]);
                    if (lastDx && lastDy) {
                        if (dx != 0) {
                            this.computeNewPnts(this, dx - lastDx, AlignType.LEFT, ctrlPnt, vcts)
                        }
                        if (dy != 0) {
                            this.computeNewPnts(this, dy - lastDy, AlignType.BOTTOM, ctrlPnt, vcts)
                        }
                    }
                    break;
                }
            default:
                break;
        }
        lastDx = dx;
        lastDy = dy;
    }

    getBboxWrapPnts(feature: Feature) {  // 获取bbox矩形的四个点坐标
        const zeroPntArr = feature.getPointArr(feature.pointArr, -feature.angle);
        const center = Feature.getCenterPos(zeroPntArr);
        const rectPntArr = Feature.getRectWrapPoints(zeroPntArr);
        const width = Utils.getLenOfTwoPnts(rectPntArr[0], rectPntArr[1])
        const height = Utils.getLenOfTwoPnts(rectPntArr[1], rectPntArr[2])
        const bboxRectPointArr = Utils.getRectPoints(center, { width, height })
        const rPointArr = this.getPointArr(bboxRectPointArr, feature.angle, Feature.getCenterPos(feature.pointArr))
        return rPointArr
    }

    //重置bbox的大小和更新控制点的位置
    resize(target: Feature = this.target) {
        this.pointArr = this.getBboxWrapPnts(target)
        const pCtrlPnts = this.children.filter(f => f.name.indexOf('pCtrlp') > -1 || f.name == 'rCtrlp');
        pCtrlPnts.forEach((ctrlP, i) => {
            switch (ctrlP.name) {
                case 'pCtrlp0': { // 左上
                    const { x, y } = this.getLeftTopPnt();
                    ctrlP.pointArr[0].x = x
                    ctrlP.pointArr[0].y = y
                    break;
                }
                case 'pCtrlp1': {  // 右上
                    const { x, y } = this.getRightTopPnt();
                    ctrlP.pointArr[0].x = x
                    ctrlP.pointArr[0].y = y
                    break;
                }
                case 'pCtrlp2': {  // 右下
                    const { x, y } = this.getRightBottomPnt();
                    ctrlP.pointArr[0].x = x
                    ctrlP.pointArr[0].y = y
                    break;
                }
                case 'pCtrlp3': { // 左下
                    const { x, y } = this.getLeftBottomPnt();
                    ctrlP.pointArr[0].x = x
                    ctrlP.pointArr[0].y = y
                    break;
                }
                case 'pCtrlp4': {   // 左中
                    ctrlP.pointArr[0] = this.getLeftMidPnt();
                    break;
                }
                case 'pCtrlp5': {  // 右中
                    ctrlP.pointArr[0] = this.getRightMidPnt();
                    break;
                }
                case 'pCtrlp6': {  // 上中
                    ctrlP.pointArr[0] = this.getTopMidPnt();
                    break;
                }
                case 'pCtrlp7': {  // 下中
                    ctrlP.pointArr[0] = this.getBottomMidPnt();
                    break;
                }
                case 'rCtrlp':
                    ctrlP.pointArr[0] = this.getRotatePnt();
                    break;
                default:
                    break;
            }
        })
    }

    // 四个控制点位置
    getLeftTopPnt() {
        return { ...this.pointArr[0] };
    }
    getRightTopPnt() {
        return { ...this.pointArr[1] };
    }
    getRightBottomPnt() {
        return { ...this.pointArr[2] };
    }
    getLeftBottomPnt() {
        return { ...this.pointArr[3] };
    }

    // 四个边中点位置
    getLeftMidPnt() {
        return { ...Utils.getMidOfTwoPnts(this.pointArr[0], this.pointArr[3]) };
    }
    getRightMidPnt() {
        return { ...Utils.getMidOfTwoPnts(this.pointArr[1], this.pointArr[2]) };
    }
    getTopMidPnt() {
        return { ...Utils.getMidOfTwoPnts(this.pointArr[0], this.pointArr[1]) };
    }
    getBottomMidPnt() {
        return { ...Utils.getMidOfTwoPnts(this.pointArr[2], this.pointArr[3]) };
    }

    getRotatePnt() {
        const vct = Utils.getVector(this.pointArr[0], this.pointArr[3]);   // 控制点1,2的向量
        const midPnt = Utils.getMidOfTwoPnts(this.pointArr[0], this.pointArr[1]);
        // ctrlP.pointArr[0] = Utils.getPntInVct(midPnt, vct, -15) // 关联点长度同步移动
        return { ...Utils.getPntInVct(midPnt, vct, -15) }
    }
}
